Skip to content

一、概述

Apache Dubbo™ (incubating)是一款高性能Java RPC框架。官网:http://dubbo.apache.org/zh-cn/index.html

DUBBO是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,是阿里巴巴SOA服务化治理方案的核心框架

1.1 架构

jiagou

节点角色说明
Provider暴露服务的服务提供方
Consumer调用远程服务的服务消费方
Registry服务注册与发现的注册中心
Monitor统计服务的调用次数和调用时间的监控中心
Container服务运行容器

调用关系说明

  1. 服务容器负责启动,加载,运行服务提供者。
  2. 服务提供者在启动时,向注册中心注册自己提供的服务。
  3. 服务消费者在启动时,向注册中心订阅自己所需的服务。
  4. 注册中心返回服务提供者地址列表给消费者,如果有变更,注册中心将基于长连接推送变更数据给消费者。
  5. 服务消费者,从提供者地址列表中,基于软负载均衡算法,选一台提供者进行调用,如果调用失败,再选另 一台调用。
  6. 服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。 Dubbo 架构具有以下几个特点,分别是连通性、健壮性、伸缩性、以及向未来架构的升级性。

1.2 特点

连通性、健壮性、伸缩性、以及向未来架构的升级性。

  • 连通性

    l 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小

    l 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示

    l 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销

    l 服务消费者向注册中心获取服务提供者地址列表,并根据负载算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销

    l 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外

    l 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者

    l 注册中心和监控中心全部宕机,不影响已运行的提供者和消费者,消费者在本地缓存了提供者列表

    注册中心和监控中心都是可选的,服务消费者可以直连服务提供者

  • 健壮性

    l 监控中心宕掉不影响使用,只是丢失部分采样数据

    l 数据库宕掉后,注册中心仍能通过缓存提供服务列表查询,但不能注册新服务

    l 注册中心对等集群,任意一台宕掉后,将自动切换到另一台

    l 注册中心全部宕掉后,服务提供者和服务消费者仍能通过本地缓存通讯

    l 服务提供者无状态,任意一台宕掉后,不影响使用

    l 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复

  • 伸缩性

    l 注册中心为对等集群,可动态增加机器部署实例,所有客户端将自动发现新的注册中心

    l 服务提供者无状态,可动态增加机器部署实例,注册中心将推送新的服务提供者信息给消费者.

  • 升级性

1.3 注册中心

dubbo支持多种注册中心,推荐使用ZooKeeper。(l Multicast 注册中心,l Redis注册中心**,l Simple注册中心**)

Zookeeper 是 Apacahe Hadoop 的子项目,是一个树型的目录服务,支持变更推送,适合作为 Dubbo 服务的注册中心,工业强度较高,可用于生产环境,并推荐使用。

zkliucheng

流程说明:

  • 服务提供者启动时: 向 /dubbo/com.foo.BarService/providers 目录下写入自己的 URL 地址
  • 服务消费者启动时: 订阅 /dubbo/com.foo.BarService/providers 目录下的提供者 URL 地址。并向/dubbo/com.foo.BarService/consumers 目录下
  • 写入自己的 URL 地址 监控中心启动时: 订阅 /dubbo/com.foo.BarService 目录下的所有提供者和消费者 URL 地址。

支持以下功能:

  • 当提供者出现断电等异常停机时,注册中心能自动删除提供者信息
  • 当注册中心重启时,能自动恢复注册数据,以及订阅请求
  • 当会话过期时,能自动恢复注册数据,以及订阅请求
  • 当设置 <dubbo:registry check="false" /> 时,记录失败注册和订阅请求,后台定时重试
  • 可通过 <dubbo:registry username="admin" password="1234" /> 设置 zookeeper 登录信息
  • 可通过 <dubbo:registry group="dubbo" /> 设置 zookeeper 的根节点,不设置将使用无根树
  • 支持 * 号通配符 <dubbo:reference group="" version="" /> ,可订阅服务的所有分组和所有版本的提供者

1.4 负载均衡

在集群负载均衡时,Dubbo 提供了多种均衡策略,缺省为 random 随机调用。可通过消费端的@Reference注解中的loadbalance属性配置。

1.5 dubbo源码分层

分层名称作用
config 配置层对外配置接口,以 ServiceConfig, ReferenceConfig 为中心,可以直接初始化配置类,也可以通过 spring 解析配置生成配置类
proxy 服务代理层服务接口透明代理,生成服务的客户端 Stub 和服务器端 Skeleton, 以ServiceProxy 为中心,扩展接口为 ProxyFactory
registry 注册中心层封装服务地址的注册与发现,以服务 URL 为中心,扩展接口为 RegistryFactory,Registry, RegistryService
cluster 路由层封装多个提供者的路由及负载均衡,并桥接注册中心,以 Invoker 为中心,扩展接口为 Cluster, Directory, Router, LoadBalance
monitor 监控层RPC 调用次数和调用时间监控,以 Statistics 为中心,扩展接口为 MonitorFactory,Monitor, MonitorService
protocol 远程调用层封装 RPC 调用,以 Invocation, Result 为中心,扩展接口为 Protocol, Invoker,Exporter
exchange 信息交换层封装请求响应模式,同步转异步,以 Request, Response 为中心,扩展接口为 Exchanger, ExchangeChannel, ExchangeClient, ExchangeServer
transport 网络传输层抽象 mina 和 netty 为统一接口,以 Message 为中心,扩展接口为 Channel,Transporter, Client, Server, Codec
serialize 数据序列化层可复用的一些工具,扩展接口为 Serialization, ObjectInput, ObjectOutput,ThreadPool

二、集成示例

2.1 springboot集成示例

提供者

依赖
xml
<!--添加SpringBoot测试-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>
<!--添加dubbo的springboot依赖-->
<dependency>
	<groupId>com.alibaba.boot</groupId>
	<artifactId>dubbo-spring-boot-starter</artifactId>
	<version>0.2.0</version>
</dependency>
<!--添加dubbo依赖-->
<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>dubbo</artifactId>
	<version>2.6.4</version>
</dependency>

<dependency>
	<groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.13</version>
</dependency>
<dependency>
	<groupId>com.github.sgroschupf</groupId>
	<artifactId>zkclient</artifactId>
	<version>0.1</version>
</dependency>
service接口
java
@Service(version = "${dubbo.service.version}") //声明这是一个dubbo服务 dubbo提供的注解@Service,而不是Spring的
public class UserServiceImpl implements UserService {
    /**
    * 实现查询,这里做模拟实现,不做具体的数据库查询
    */
    public List<User> queryAll() {
        List<User> list = new ArrayList<User>();
        for (int i = 0; i < 10; i++) {
            User user = new User();
            user.setAge(10 + i);
            user.setId(Long.valueOf(i + 1));
            user.setPassword("123456");
            user.setUsername("username_" + i);
            list.add(user);
        }
        return list;
    }
}
配置
properties
# Spring boot application


<NolebasePageProperties />




spring.application.name = itcast-dubbo-service
server.port = 9090
# Service version
dubbo.service.version = 1.0.0
# 服务的扫描包
dubbo.scan.basePackages = cn.itcast.dubbo.service
# 应用名称
dubbo.application.name = dubbo-provider-demo
# 协议以及端口
dubbo.protocol.name = dubbo
dubbo.protocol.port = 20880
# zk注册中心
dubbo.registry.address = zookeeper://172.16.55.185:2181
dubbo.registry.client = zkclient

spring.application.name=dubbo-demo-provider
spring.dubbo.application.id=dubbo-demo-provider
spring.dubbo.application.name=dubbo-demo-provider
spring.dubbo.registry.address=zookeeper://192.168.25.140:2181;zookeeper://192.168.25.140:2182;zookeeper://192.168.25.140:2183
spring.dubbo.server=true
spring.dubbo.protocol.name=dubbo
spring.dubbo.protocol.port=20880
启动类
java
// 需添加@EnableDubboConfiguration注解
new SpringApplicationBuilder(DubboProvider.class)
.web(WebApplicationType.NONE) // 非 Web 应用
.run(args);

消费者

依赖
xml
<dependency>
    <groupId>org.apache.zookeeper</groupId>
	<artifactId>zookeeper</artifactId>
	<version>3.4.13</version>
</dependency>
<dependency>
	<groupId>com.github.sgroschupf</groupId>
	<artifactId>zkclient</artifactId>
	<version>0.1</version>
</dependency>
<!--引入service的依赖-->
<dependency>
	<groupId>cn.itcast.dubbo</groupId>
	<artifactId>itcast-dubbo-service</artifactId>
	<version>1.0-SNAPSHOT</version>
</dependency>
测试
java
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestUserService {
    // loadbalance 负载均衡策略
    @Reference(version = "1.0.0" , loadbalance = "roundrobin")
    private UserService userService;
    @Test
    public void testQueryAll(){
    	List<User> users = this.userService.queryAll();
        for (User user : users) {
        	System.out.println(user);
        }
    }
}
配置
properties
# Spring boot application
spring.application.name = itcast-dubbo-consumer
server.port = 9091
# 应用名称
dubbo.application.name = dubbo-consumer-demo
spring.dubbo.application.id=dubbo-demo-consumer
# zk注册中心
dubbo.registry.address = zookeeper://172.16.55.185:2181
dubbo.registry.client = zkclient

三、dobbo特性

3.1 地址缓存

dubbo服务消费者在第一次调用时,会将服务提供方地址缓存到本地,以后在调用则不会访问注册中心。

当服务提供者地址发生变化时,注册中心会通知服务消费者。

3.2 超时与重试

dubbo 利用超时机制来解决大量请求阻塞的问题,•设置一个超时时间,在这个时间段内,无法完成服务访问,则自动断开连接。

使用timeout属性配置超时时间,默认值1000,单位毫秒。

如果出现网络抖动,则这一次请求就会失败。

Dubbo 提供重试机制来避免网络抖动使请求失败的问题。

通过 retries 属性来设置重试次数。默认为 2 次。

3.3 多版本

灰度发布:当出现新功能时,会让一部分用户先使用新功能,用户反馈没问题时,再将所有用户迁移到新功能。

dubbo 中使用version 属性来设置和调用同一个接口的不同版本

3.4 负载均衡

负载均衡策略(4种) :

Random :按权重随机,默认值。按权重设置随机概率。

RoundRobin :按权重轮询

LeastActive:最少活跃调用数,相同活跃数的随机。

ConsistentHash:一致性 Hash,相同参数的请求总是发到同一提供者。

3.5 集群容错

服务路由

服务路由包含一条路由规则,路由规则决定了服务消费者的调用目标,即规定了服务消费者可调用哪些服务提供者,dubbo提供三种服务路由实现,分别为条件路由ConditionRouter、脚本路由ScriptRouter、标签路由TagRouter

条件路由

条件路由规则的格式:[服务消费者匹配条件] => [服务提供者匹配条件]

host = 10.20.153.10 => host = 10.20.153.11

该条规则表示 IP 为 10.20.153.10 的服务消费者只可调用 IP 为 10.20.153.11 机器上的服务,不可调用其他机器上的服务。

如果服务消费者匹配条件为空,表示不对服务消费者进行限制。如果服务提供者匹配条件为空,表示对某些服务消费者禁用服务

常见路由配置

配置类型配置示例
白名单host!=10.20.153.10,10.20.153.11=>
黑名单host=10.20.153.10,10.20.153.11=>
读写分离method=find,list,get,is=>host=172.22.3.94,172.22.3.95,172.22.3.96
method!=find,list,get,is=>host=172.22.3.97,172.22.3.98
前后台分离application=front=>host=172.22.3.91,172.22.3.92,172.22.3.93
application!=front=>host=172.22.3.94,172.22.3.95,172.22.3.96

集群容错模式

模式名称描述
Failover Cluster失败重试。默认值。当出现失败,重试其它服务器 ,默认重试2次,使用 retries 配置。一般用于读操作
Failfast Cluster快速失败,只发起一次调用,失败立即报错。通常用于写操作
Failsafe Cluster失败安全,出现异常时,直接忽略。返回一个空结果。
Failback Cluster失败自动恢复,后台记录失败请求,定时重发。通常用于消息通知操作。
Forking Cluster并行调用多个服务器,只要一个成功即返回。
Broadcast Cluster广播调用所有提供者,逐个调用,任意一台报错则报错

3.6 服务降级

使用dubbo在进行服务调用时,可能由于各种原因(服务器宕机/网络超时/并发数太高等),调用中就会出现RpcException,调用失败。

可以通过服务降级功能临时屏蔽某个出错的非关键服务,并定义降级后的返回策略

服务降级就是指在由于非业务异常导致的服务不可用时,可以返回默认值,避免异常影响主业务的处理。

可以向注册中心写入动态配置覆盖规则:

mock=force:return+null 表示消费方对该服务的方法调用都直接返回 null 值,不发起远程调用。用来屏蔽不重要服务不可用时对调用方的影响。

还可以改为 mock=fail:return+null 表示消费方对该服务的方法调用在失败后,再返回null 值,不抛异常。用来容忍不重要服务不稳定时对调用方的影响。

mock配置方式

dubbo官方文档上使用一个mock配置,实现服务降级。mock只在出现非业务异常(比如超时,网络异常等)时执行。mock的配置支持两种,一种为boolean值,默认的为false。如果配置为true,则缺省使用mock类名,即类名+Mock后缀;另外一种则是配置”return null”,可以很简单的忽略掉异常。

mock配置在调用方,服务降级不需要对服务方配置产生修改。下面举个例子说明,mock的配置

xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
        xsi:schemaLocation="http://www.springframework.org/schema/beans        http://www.springframework.org/schema/beans/spring-beans.xsd        http://code.alibabatech.com/schema/dubbo        http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
     
        <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
        <dubbo:application name="dubbo-consumer"  />
     
        <dubbo:registry address="zookeeper://127.0.0.1:2181" />
     
        <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
        <dubbo:reference id="fooService" interface="com.test.service.FooService"  timeout="10000" check="false" mock="return null">
        </dubbo:reference>
     
    </beans>

测试在调用端调用服务两个方法,当服务端正常启动时,程序获得正常返回值;当服务提供方没有启动(模拟服务不可用状态),调用方依然正常运行,调用doSomething2获取返回值时null。

mock实现接口方式

mock配置方式,在服务降级时会对service中的所有方法做统一处理,即都返回null。但是有的时候需要一些方法在服务不可用时返回一些其他信息,以便做其他处理。如更新/删除等。

配置mock="true" ,同时实现mock接口,类名要注意命名规范:接口名+Mock后缀。此时如果调用失败会调用Mock实现。mock实现需要保证有无参的构造方法。

整合hystrix

Hystrix 旨在通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备拥有回退机制和断路器功能的线程和信号隔离,请求缓存和请求打包,以及监控和配置等功能

1、springboot官方提供了对hystrix的集成,直接在pom.xml里加入依赖

2、在主程序类上增加注解@EnableHystrix来开启hystrix功能

3、在服务提供者Provider上增加@HystrixCommand注解,这样调用就会经过Hystrix代理

4、在服务消费者增加@HystrixCommand(fallbackMethod= methodName))注解并指定出错时的回调方法。当调用出错时,会调用fallbackMethod = "hello"里面的指定的hello方法

3.7 dubbo线程IO模型

  • BIO

    每个客户端连接过来后,服务端都会启动一个线程去处理该客户端的请求。阻塞I/O每个连接创建成功之后都需要一个线程来维护。

  • NIO

    NIO,也叫做new-IO或者non-blocking-IO,可理解为非阻塞IO。NIO编程模型中,新来一个连接不再创建一个新的线程,而是可以把这条连接直接绑定到某个固定的线程,然后这条连接所有的读写都由这个线程来负责

    IO模型中,一个连接都会创建一个线程,对应一个while死循环,死循环的目的就是不断监测这条连接上是否有数据可以读。但是在大多数情况下,1万个连接里面同一时刻只有少量的连接有数据可读,因此,很多个while死循环都白白浪费掉了,因为没有数据。 而在NIO模型中,可以把这么多的while死循环变成一个死循环,这个死循环由一个线程控制。这就是NIO模型中选择器(Selector)的作用,一条连接来了之后,现在不创建一个while死循环去监听是否有数据可读了,而是直接把这条连接注册到选择器上,通过检查这个选择器,就可以批量监测出有数据可读的连接,进而读取数据。

    NIO模型解决线程资源受限的方案,实际开发过程中,我们会开多个线程,每个线程都管理着一批连接,相对于IO模型中一个线程管理一条连接,消耗的线程资源大幅减少。

四、SPI

SPI 全称为 Service Provider Interface,是一种服务发现机制。SPI 的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。这样可以在运行时,动态为接口替换实现类。正因此特性,我们可以很容易的通过 SPI 机制为我们的程序提供拓展功能。SPI 机制在第三方框架中也有所应用,比如 Dubbo 就是通过 SPI 机制加载所有的组件。不过,Dubbo 并未使用 Java 原生的SPI 机制,而是对其进行了增强,使其能够更好的满足需求。在 Dubbo 中,SPI 是一个非常重要的模块。

java原生spi

在resources中新建/META-INF/services目录,在新建的目录中新建文件,注意文件名必须是接口的全类名,文件中写入接口的实现类,每一个实现类占一行,即可使起加载类。

dubbo SPI机制

Dubbo 重新实现了一套功能更强的 SPI 机制,Dubbo SPI 的相关逻辑被封装在了 ExtensionLoader 类中,通过 ExtensionLoader,我们可以加载指定的实现类。Dubbo SPI 所需的配置文件需放置在META-INF/dubbo 路径下,配置内容如下

college=cn.itheima.api.impl.Czxy
shortTrain=cn.itheima.api.impl.Itheima

与 Java SPI 实现类配置不同,Dubbo SPI 是通过键值对的方式进行配置,这样我们可以按需加载指定的实现类。另外,在测试 Dubbo SPI 时,需要在 Robot 接口上标注 @SPI 注解。

五、安装

5.1 dubbo-admin安装

dubbo-admin 是一个前后端分离的项目。前端使用vue,后端使用springboot,安装 dubbo-admin 其实就是部署该项目。我们将dubbo-admin安装到开发环境上。要保证开发环境有jdk,maven,nodejs

安装node

(如果当前机器已经安装请忽略)

因为前端工程是用vue开发的,所以需要安装node.js,node.js中自带了npm,后面我们会通过npm启动

下载地址

https://nodejs.org/en/

Dubbo-Admin

进入github,搜索dubbo-admin

https://github.com/apache/dubbo-admin
  1. 解压后修改配置文件

    properties
    # application.properties
    # centers in dubbo2.7 
    # 修改zookeeper地址
    # 注册中心
    admin.registry.address=zookeeper://192.168.149.135:2181
    # 配置中心
    admin.config-center=zookeeper://192.168.149.135:2181
    # 元数据中心
    admin.metadata-report.address=zookeeper://192.168.149.135:2181
  2. 打包

    在 dubbo-admin-develop 目录执行打包命令mvn clean package

  3. 启动

    sh
    # 在dubbo-Admin-develop\dubbo-admin-distribution\target>目录
    java -jar .\dubbo-admin-0.1.jar
    
    # dubbo-admin-ui目录
    npm run dev
  4. 访问

    html
    # 用户名密码都是root
    http://localhost:8081/